﻿using System.Reflection;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

using zijian666.Core;
using zijian666.DI.Features;

namespace zijian666.DI;

/// <summary>
/// 启动器, 调用启动类的各种方法
/// </summary>
/// <param name="startupOrType">启动类实例或类型 <seealso cref="Type"/></param>
public class Launcher(object startupOrType) : ILauncher
{
    private object _startup = startupOrType ?? throw new ArgumentNullException(nameof(startupOrType));

    private Task? _configureServicesed;
    private Task? _configured;
    private Task? _stoped;

    /// <summary>
    /// 启动类的实际类型
    /// </summary>
    public Type StartupType => _startup as Type ?? _startup.GetType();

    /// <summary>
    /// 启动类的实例
    /// </summary>
    public object? Startup => _startup is Type ? null : _startup;

    /// <inheritdoc />
    public Task ConfigureServicesAsync(IServiceCollection services, object[]? args, CancellationToken cancellationToken)
        => OnceCaller.ExecuteAsync(ref _configureServicesed, async () =>
        {
            var logger = services.Where(x => x.ServiceType == typeof(ILogger)).FirstOrDefault();
            var filter = FeatureManager.Gets<IConfigureServicesFilter>().ToList();
            filter.ForEach(x => x.BeginConfigureServices(services, StartupType, args ?? []));
            var provider = new ServiceProviderProxy()
                                .AddServices(args)
                                .AddServices(services.GetImplementationServices());

            if (_startup is Type type)
            {
                provider.AddService(ServiceDescriptor.Singleton(type, p =>
                {
                    var service = ActivatorUtilities.CreateInstance(p, type);
                    services.AddSingleton(type, service);
                    return service;
                }));
            }

            await CallAsync<ConfigureServicesAttribute>(provider, ConfigureServicesAttribute.MatchNames, cancellationToken);
            filter.ForEach(x => x.EndConfigureServices(services, StartupType, args ?? []));
        });

    /// <inheritdoc />
    public Task ConfigureAsync(IServiceProvider provider, object[]? args, CancellationToken cancellationToken)
        => OnceCaller.ExecuteAsync(ref _configured, async () =>
        {
            var logger = provider.GetService<ILogger<Launcher>>();
            using var _ = logger?.BeginScope("ConfigureAsync");
            logger?.LogInformation($"正在配置 {StartupType.FullName}");
            var filter = FeatureManager.Gets<IConfigureServicesFilter>().ToList();
            filter.ForEach(x => x.BeginConfigure(provider, StartupType, args ?? []));
            await CallAsync<ConfigureAttribute>(new ServiceProviderProxy(provider, args), ConfigureAttribute.MatchNames, cancellationToken);
            filter.ForEach(x => x.EndConfigure(provider, StartupType, args ?? []));
            logger?.LogInformation($"已配置 {StartupType.FullName}");
        });

    /// <inheritdoc />
    public Task StopAsync(IServiceProvider provider, object[]? args, CancellationToken cancellationToken)
        => OnceCaller.ExecuteAsync(ref _stoped,
                async () =>
                {
                    var logger = provider.GetService<ILogger<Launcher>>();
                    using var _ = logger?.BeginScope("StopAsync");
                    logger?.LogInformation($"正在停止 {StartupType.FullName}");
                    await CallAsync<HostStopAttribute>(new ServiceProviderProxy(provider, args), HostStopAttribute.MatchNames, cancellationToken);
                    logger?.LogInformation($"已停止 {StartupType.FullName}");
                });

    /// <summary>
    /// 调用指定方法
    /// </summary>
    private async Task CallAsync<T>(IServiceProvider provider, string[] matchNames, CancellationToken cancellationToken)
        where T : Attribute
    {
        var methods = StartupType.GetMethods();
        var nothing = true;
        foreach (var method in methods)
        {
            cancellationToken.ThrowIfCancellationRequested();

            if (method.IsDefined(typeof(T)))
            {

            }
            else if (method.GetCustomAttributes(false).Any(x => x is IHostAspectAttribute)
                || !matchNames.Contains(method.Name, StringComparer.OrdinalIgnoreCase))
            {
                continue;
            }

            nothing = false;

            try
            {
                await provider.InvokeAsync(method, cancellationToken);
            }
            catch (Exception e)
            {
                throw new MethodAccessException($"执行方法 ({(method.ReflectedType ?? method.DeclaringType)?.FullName}.{method.Name}) 出现异常: {e.Message}", e);
            }
        }
        if (nothing)
        {
            var logger = provider.GetService<ILogger<Launcher>>();
            logger?.LogInformation($"没有需要执行的方法");
            return;
        }
    }
}
